
#include "gkt_backboard.h"
#include "bb_protocol.h"
#include "bb_module_common.h"
#include "gkt_debug.h"
#include "gkt_list.h"
#include "gkt_os.h"
#include "gkt_uart.h"
#include "gkt_gpio.h"
#include <string.h>

#ifdef GKT_CONFIG_BACKBOARD_THREAD_PRIORITY
#define BB_THREAD_PRIORITY	GKT_CONFIG_BACKBOARD_THREAD_PRIORITY
#else
#define BB_THREAD_PRIORITY	NORMAL
#endif

#ifdef GKT_CONFIG_BACKBOARD_THREAD_STACKSIZE
#define BB_THREAD_STACKSIZE	GKT_CONFIG_BACKBOARD_THREAD_STACKSIZE
#else
#define BB_THREAD_STACKSIZE	0x1000
#endif

#define BB_MUTEX_TIMEOUT	100

#define BB_SEND_HEARTBEAT_INTERVAL_TIME 2000

typedef struct _bb_send_job bb_send_job_s;
struct _bb_send_job {
	gkt_list_s	link;

	union {
		struct {
			uint16_t	length;
		} common;
		struct {
			uint16_t	length;
			uint8_t	data[BB_NETWORK_PAYLOAD_MINSIZE + GKT_INTSIZE];
		} simple;
		struct {
			uint16_t	length;
			uint8_t	unused[2];
			const uint8_t	*data;
		} normal;
	};
};

typedef struct _bb_module {
	uint32_t	opcode_max;
	gkt_bb_module_notify_fn	notify;
} bb_module_s;

#define BB_RX_FRAME_NUMS	8
typedef struct _bb_rx_frame_info {
	uint32_t	length;
	const uint8_t	*data;
} bb_rx_frame_info_s;

#define BB_SEND_JOB_MAXNUMS	(GKT_BB_MODULE_MAX + 2)
typedef struct {
	int	init_ok;

	gkt_mutex_t	mutex;

	volatile int	send_busy;
	gkt_list_s	send_pending_jobs_head;
	gkt_list_s	send_free_jobs_head;
	bb_send_job_s	send_jobs[BB_SEND_JOB_MAXNUMS];

	gkt_semaphore_t	rx_semaphore;
	uint32_t	rx_frame_in_pos;
	uint32_t	rx_frame_out_pos;
	bb_rx_frame_info_s	rx_frame_infos[BB_RX_FRAME_NUMS];
	uint8_t	rx_frame_data[BB_NETWORK_PACKET_MAXLEN];
	uint32_t	rx_frame_length;
} backboard_s;
static backboard_s s_backboard;

static void bb_application_packet_received(
				const uint8_t *data, uint32_t length)
{
	bb_module_s *module;
	uint8_t opcode;

	if (length >= BB_NETWORK_PAYLOAD_MINSIZE) {
		opcode = data[BB_NETWORK_PAYLOAD_OPCODE_OFFSET];

		module = &s_backboard.modules[GKT_BB_MODULE_NETWORK];
		opcode = data[BB_APPLICATION_OPCODE_OFFSET];
		if (opcode >= module->opcode_max) {
			gkt_error("bb_application: <RX> opcode(%u) overflow!\n", opcode);
			return;
		}

		gkt_info("bb_application: <RX> opcode(%u) length(%u)\n",
						opcode, length);
		if (module->notify)
			(*module->notify)(GKT_BB_NOTIFY_PEER, data, length);
	}
}

#ifdef GKT_CONFIG_BACKBOARD_UART_ID

#ifdef GKT_CONFIG_BB_NETWORK_SEND_MAXTIMES
#define BB_NETWORK_SEND_MAXTIMES GKT_CONFIG_BB_NETWORK_SEND_MAXTIMES
#else
#define BB_NETWORK_SEND_MAXTIMES 3
#endif
static uint8_t bb_network_checksum(uint8_t *buff, uint32_t length)
{
	int sum = 0;
	int i;
	if (NULL != buff) {
		for (i=0; i<length; i++)
			sum += buff[i];
		sum %= 256;
	}
	return sum;
}

int bb_network_send_packet(uint8_t *payload_buff, uint32_t length)
{
	uint8_t *net_packet;
	uint32_t net_packet_len;
	int retval, loops;
	int i;

	net_packet = (uint8_t*)gkt_calloc(BB_NETWORK_PACKET_MINSIZE + length);
	if (!net_packet) 
		return GKT_ENOMEMORY;

	gkt_store_be16(net_packet, BB_NETWORK_PREAMBLE);
	net_packet[BB_NETWORK_VERSION_OFFSET] = BB_NETWORK_VERSION;
	memcpy(&net_packet[BB_NETWORK_OPCODE_OFFSET], payload_buff, length);
	net_packet[BB_NETWORK_PACKET_SIZE(length)-1] = 
		bb_network_checksum(net_packet, BB_NETWORK_PACKET_SIZE(length)-1);

	net_packet_len = BB_NETWORK_PACKET_SIZE(length) + 1;

	gkt_printf("send: net_packet_len(%d), net_packet:", net_packet_len);
	for (i=0; i<net_packet_len; i++) {
		gkt_printf("0x%02x ", net_packet[i]);
	}
	gkt_printf("\n");

	loops = 0;
	do {
		retval = gkt_uart_send(GKT_CONFIG_BACKBOARD_UART_ID, 
						net_packet, net_packet_len, 0);

		if (GKT_SUCCESS == retval)
			break;
		else
			gkt_error("bb_network_send_packet: gkt_uart_send failed, retval(%d)\n", retval);

		gkt_delay_us(10);
	} while (++loops < BB_NETWORK_SEND_MAXTIMES);

	gkt_free(net_packet);
	net_packet = NULL;

	return retval;
}

void bb_network_packet_received(const uint8_t *data, 
				uint32_t length)
{
	const uint8_t *net_packet_data, *net_payload;
	uint32_t preamble, offset, net_packet_len;
	uint8_t net_data_len, checksum_fetch, checksum_calc;

	gkt_printf("received: length(%d), net_payload:", length);
	for (int i=0; i<length; i++) {
		gkt_printf("0x%02x ", data[i]);
	}
	gkt_printf("\n");

	offset = 0;
	while ((offset + BB_NETWORK_PACKET_MINSIZE) <= length) {
		net_packet_data = &data[offset];
		
		preamble = gkt_fetch_be16(&net_packet_data[BB_NETWORK_PREAMBLE_OFFSET]);
		if (preamble != BB_NETWORK_PREAMBLE) {
			offset++;
			continue;
		}
		net_data_len = net_packet_data[BB_NETWORK_DATA_LENGTH_OFFSET];
		net_packet_len = BB_NETWORK_PACKET_MINSIZE + net_data_len;
		if ((offset + net_packet_len) <= length) {
			checksum_calc = bb_network_checksum(net_packet_data, net_packet_len-1);
			checksum_fetch = net_packet_data[net_packet_len-1];
			if (checksum_calc == checksum_fetch) {
				net_payload = &net_packet_data[BB_NETWORK_OPCODE_OFFSET];
				bb_application_packet_received(net_payload, BB_NETWORK_PAYLOAD_MINSIZE+net_data_len);
			}
			else {
				gkt_error("net_packet: [%u] checksum(0x%x-0x%x) unmatch!\n", 
					offset, checksum_calc, checksum_fetch);
			}

			offset += net_packet_len;
		}
		else
			break;	/* incomplete network packet */
	}

	if (offset < length) {
		// TODO: data surplus
	}
}

#ifdef GKT_CONFIG_BACKBOARD_UART_BAUDRATE
#define BB_UART_BAUDRATE GKT_CONFIG_BACKBOARD_UART_BAUDRATE
#else
#define BB_UART_BAUDRATE 115200
#endif
static void bb_uart_rx_callbak(gkt_uart_callback_attr_t *attr)
{
	uint32_t frame_nums, pos;

	if ((attr->function == GKT_UART_FUNCTION_RX)
		&& (attr->size > 0)
		&& (attr->size <= BB_NETWORK_PACKET_MAXLEN)
		&& attr->buf_offset) 
	{
		gkt_info("bb_uart_rx_callbak: rx_frame_in(%d) - rx_frame_out(%d) - data_size(%d)\n", 
				s_backboard.rx_frame_in_pos, s_backboard.rx_frame_out_pos, attr->size);
		frame_nums = s_backboard.rx_frame_in_pos - s_backboard.rx_frame_out_pos;
		if (frame_nums < BB_RX_FRAME_NUMS) {
			pos = s_backboard.rx_frame_in_pos % BB_RX_FRAME_NUMS;
			s_backboard.rx_frame_infos[pos].length = attr->size;
			s_backboard.rx_frame_infos[pos].data = attr->buf_offset;
			s_backboard.rx_frame_in_pos++;
			gkt_semaphore_release(s_backboard.rx_semaphore);
		}
	}
}

static int bb_uart_init(void)
{
	gkt_uart_open_params_s open_params;
	gkt_uart_config_params_s config_params;
	int retval;

	retval = gkt_uart_init();
	if (retval != GKT_SUCCESS) {
		gkt_error("bb_uart: init uart-%u failed %d\n", 
			GKT_CONFIG_BACKBOARD_UART_ID, retval);
		return retval;
	}
	open_params.baud_rate = BB_UART_BAUDRATE;
	open_params.data_bits = 8;
	open_params.flow_control = GKT_UART_FLOWCTRL_NONE;
	open_params.parity = GKT_UART_PARITY_NONE;
	open_params.stop_bits = GKT_UART_STOPBITS_1;
	retval = gkt_uart_open(GKT_CONFIG_BACKBOARD_UART_ID, &open_params);
	if (retval != GKT_SUCCESS) {
		gkt_error("bb_uart: open uart-%u failed %d\n", 
			GKT_CONFIG_BACKBOARD_UART_ID, retval);
		return retval;
	}

	config_params.buffer = NULL;
	config_params.buff_size = 1024;
	config_params.frame_size = BB_NETWORK_PACKET_MAXLEN;
	config_params.callback = bb_uart_rx_callbak;
	retval = gkt_uart_config(GKT_CONFIG_BACKBOARD_UART_ID, 
				GKT_UART_FUNCTION_RX_TX, &config_params);
	if (retval != GKT_SUCCESS) {
		gkt_error("bb_uart: config uart-%u failed %d\n", 
			GKT_CONFIG_BACKBOARD_UART_ID, retval);
		return retval;
	}
	
	gkt_uart_rx_enable(GKT_CONFIG_BACKBOARD_UART_ID);

	return GKT_SUCCESS;
}
#endif

static void backboard_receive_task(void *params)
{
	uint32_t pos;
	int retval;

	while (1) {
		retval = gkt_semaphore_acquire(s_backboard.rx_semaphore, GKT_INFINITE);
		if ((GKT_SUCCESS == retval)
			&& (s_backboard.rx_frame_out_pos < s_backboard.rx_frame_in_pos)) 
		{
			pos = s_backboard.rx_frame_out_pos % BB_RX_FRAME_NUMS;
			s_backboard.rx_frame_length = s_backboard.rx_frame_infos[pos].length;
			memcpy(s_backboard.rx_frame_data, s_backboard.rx_frame_infos[pos].data,
				s_backboard.rx_frame_length);

			bb_network_packet_received(s_backboard.rx_frame_data, 
				s_backboard.rx_frame_length);

			s_backboard.rx_frame_out_pos++;
		}
	}
}

static int backboard_send_insert_job(const uint8_t *data, 
				uint32_t length)
{
	gkt_list_s *list_pos;
	bb_send_job_s *send_job;
	uint32_t i;

	gkt_trace("backboard_send_insert_job: id(%u - %u), len(%u)\n",
		data[0], data[1], length);

	if (gkt_list_is_empty(s_backboard.send_free_jobs_head)) {
		gkt_error("backboard_send_insert_job: <TX> no available job!\n");
		return GKT_ENORESOURCE;
	}

	list_pos = gkt_list_first(s_backboard.send_free_jobs_head);
	gkt_list_delete(list_pos);

	send_job = __CONTAINER_OF(list_pos, bb_send_job_s, link);
	send_job->common.length = (uint16_t)length;
	if (length <= (BB_NETWORK_PAYLOAD_MINSIZE + GKT_INTSIZE)) {
		for (i = 0; i < length; i++)
			send_job->simple.data[i] = data[i];
	}
	else
		send_job->normal.data = data;
	gkt_list_add_tail(&s_backboard.send_pending_jobs_head, list_pos);

	return GKT_EINPROCESS;
}

static void backboard_send_task(void *params)
{
	gkt_list_s *list_pos;
	bb_send_job_s *send_job;
	const uint8_t *send_data;
	uint32_t module_id;
	int retval;

	gkt_unused(params);

	do {
		gkt_thread_sleep(10);

		gkt_mutex_lock(s_backboard.mutex, GKT_INFINITE);
		if (!s_backboard.send_busy) {
			while (gkt_list_is_not_empty(s_backboard.send_pending_jobs_head)) {
				list_pos = gkt_list_first(s_backboard.send_pending_jobs_head);
				gkt_list_delete(list_pos);
				s_backboard.send_busy = 1;
				gkt_mutex_unlock(s_backboard.mutex);

				send_job = __CONTAINER_OF(list_pos, bb_send_job_s, link);
				if (send_job->common.length <= (BB_NETWORK_PAYLOAD_MINSIZE + GKT_INTSIZE))
					send_data = &send_job->simple.data[0];
				else
					send_data = send_job->normal.data;
				gkt_trace("backboard_send_task: id(%u), len(%u)\n",
					send_data[0], send_job->common.length);
				retval = bb_network_send_packet(send_data, send_job->common.length);
				if (retval != GKT_SUCCESS) {
					module_id = GKT_BB_MODULE_NETWORK;
					if (s_backboard.modules[module_id].notify) {
						(*s_backboard.modules[module_id].notify)(
								GKT_BB_NOTIFY_LOCAL, send_data, retval);
					}
				}

				gkt_mutex_lock(s_backboard.mutex, GKT_INFINITE);
				gkt_list_add_tail(&s_backboard.send_free_jobs_head, list_pos);
			}

			s_backboard.send_busy = 0;
		}
		gkt_mutex_unlock(s_backboard.mutex);
	} while (1);
}

static int backboard_send_data(const uint8_t *data, uint32_t length)
{
	int retval;

	if (!s_backboard.init_ok) {
		gkt_error("backboard: <TX> not init!\n");
		return GKT_ENOTINIT;
	}

	retval = gkt_mutex_lock(s_backboard.mutex, BB_MUTEX_TIMEOUT);
	if (retval!= GKT_SUCCESS) {
		gkt_error("backboard: <TX> lock mutex failed(%d)\n", retval);
		return retval;
	}

	gkt_trace("backboard_send_data: send_busy(%d)\n", s_backboard.send_busy);
	if (!s_backboard.send_busy) {
		s_backboard.send_busy = 1;
		gkt_mutex_unlock(s_backboard.mutex);

		retval = bb_transport_send_data(data, length);
		s_backboard.send_busy = 0;
	}
	else {
		retval = backboard_send_insert_job(data, length);
		gkt_mutex_unlock(s_backboard.mutex);
	}

	return retval;
}

int gkt_backboard_send_data(const uint8_t *data, uint32_t length)
{
	uint8_t module_id, opcode;

	if (!data 
		|| (length < GKT_BB_APP_PACKET_MINSIZE)
		|| (length > BB_APPLICATION_MAXSIZE))
	{
		gkt_error("backboard: <TX> params invalid!\n");
		return GKT_EPARAM;
	}

	module_id = data[BB_APPLICATION_MODULE_ID_OFFSET];
	if (module_id >= GKT_BB_MODULE_MAX) {
		gkt_error("backboard: <TX> module_id invalid!\n");
		return GKT_EPARAM;
	}
	opcode = data[BB_APPLICATION_OPCODE_OFFSET];
	if ((uint32_t)opcode >= s_backboard.modules[module_id].opcode_max) {
		gkt_error("backboard: <TX> opcode overflow!\n");
		return GKT_EPARAM;
	}

	return backboard_send_data(data, length);
}

int gkt_backboard_is_ready(void)
{
	return bb_common_is_ready();
}

void gkt_backboard_register_module(
			gkt_bb_module_e module_id, uint32_t opcode_max,
			const gkt_bb_module_notify_fn notify)
{
	if ((module_id < GKT_BB_MODULE_MAX) 
		&& (opcode_max <= GKT_BB_OPCODE_MAX))
	{
		s_backboard.modules[module_id].opcode_max = opcode_max;
		s_backboard.modules[module_id].notify = notify;
	}
	else {
		gkt_error("backboard_register_module: params invalid!\n");
	}
}

int gkt_backboard_init(void)
{
	gkt_thread_t thread;
	uint32_t i;
	int retval;

	s_backboard.init_ok = 0;
	s_backboard.send_busy = 0;
	s_backboard.rx_frame_in_pos = 0;
	s_backboard.rx_frame_out_pos = 0;
	gkt_list_init(&s_backboard.send_pending_jobs_head);
	gkt_list_init(&s_backboard.send_free_jobs_head);
	for (i = 0; i < BB_SEND_JOB_MAXNUMS; i++) {
		gkt_list_add_tail(&s_backboard.send_free_jobs_head, 
				&s_backboard.send_jobs[i].link);
	}

	s_backboard.mutex = gkt_mutex_create(0);
	if (!s_backboard.mutex) {
		gkt_error("backboard_init: create mutex failed!\n");
		return GKT_ECREATE;
	}
	
	s_backboard.rx_semaphore = gkt_semaphore_create(BB_RX_FRAME_NUMS, 0);
	if (!s_backboard.rx_semaphore) {
		gkt_error("backboard_init: create rx_semaphore failed!\n");
		return GKT_ECREATE;
	}

	thread = gkt_thread_create(backboard_receive_task, NULL, 
					GKT_THREAD_PRIORITY_BY_NAME(BB_THREAD_PRIORITY),
					BB_THREAD_STACKSIZE, NULL);
	if (!thread) {
		gkt_error("backboard_init: create receive_task failed!\n");
		return GKT_ECREATE;
	}

	thread = gkt_thread_create(backboard_send_task, NULL, 
					GKT_THREAD_PRIORITY_BY_NAME(BB_THREAD_PRIORITY),
					BB_THREAD_STACKSIZE, NULL);
	if (!thread) {
		gkt_error("backboard_init: create send_task failed!\n");
		return GKT_ECREATE;
	}

	retval = bb_uart_init();
	if (retval != GKT_SUCCESS)
		return retval;

	s_backboard.init_ok = 1;

	return GKT_SUCCESS;
}
#ifdef GKT_CONFIG_BACKBOARD_POWER_PIN
void backboard_power_up(void)
{
	int retval;
	retval = gkt_gpio_config(GKT_CONFIG_BACKBOARD_POWER_PIN, GKT_GPIO_F_OUTPUT, NULL);
	if (retval != GKT_SUCCESS) {
			gkt_error("backboard_power_up: config pin failed: %d\n", retval);
			return;
	}
	retval = gkt_gpio_set_pin_level(GKT_CONFIG_BACKBOARD_POWER_PIN, 1);
	if (retval != GKT_SUCCESS) {
			gkt_error("backboard_power_up: set pin level failed: %d\n", retval);
			return;
	}
}
GKT_GPIO_INIT_END_HOOK_DEFINE(backboard_power_up, BASE);
#endif
